//! Integration tests for all step types

use ai_test_platform::models::{
    step::{StepContext, StepType},
    Value,
};
use ai_test_platform::steps::{
    websocket::WebSocketStep,
    thinktime::ThinkTimeStep,
    transaction::TransactionStep,
    api::ApiStep,
    testcase::TestCaseStep,
};
use ai_test_platform::models::step::IStep;
use std::time::{Duration, Instant};
use tempfile::NamedTempFile;
use std::io::Write;
use uuid::Uuid;

#[tokio::test]
async fn test_websocket_step_connect() {
    let step = WebSocketStep::connect(
        "ws://localhost:8080/test".to_string(),
        Some("test_connection".to_string())
    );
    
    let mut context = StepContext::new(Uuid::new_v4(), Uuid::new_v4());
    let result = step.execute(&mut context).await.unwrap();
    
    assert!(result.success);
    assert_eq!(result.step_type, StepType::WebSocket);
    
    // Check that connection was stored in context
    assert!(context.variables.contains_key("websocket_connection_test_connection"));
    
    // Check response data
    if let Some(Value::Object(data)) = result.data {
        assert_eq!(data.get("action").unwrap().as_str().unwrap(), "connect");
        assert_eq!(data.get("url").unwrap().as_str().unwrap(), "ws://localhost:8080/test");
        assert_eq!(data.get("status").unwrap().as_str().unwrap(), "connected");
    } else {
        panic!("Expected response data");
    }
}

#[tokio::test]
async fn test_websocket_step_send() {
    let message = Value::String("Hello WebSocket!".to_string());
    let step = WebSocketStep::send(message.clone(), Some("test_connection".to_string()));
    
    let mut context = StepContext::new(Uuid::new_v4(), Uuid::new_v4());
    // Simulate existing connection
    context.variables.insert(
        "websocket_connection_test_connection".to_string(),
        Value::String("ws://localhost:8080/test".to_string())
    );

    let result = step.execute(&mut context).await.unwrap();

    assert!(result.success);
    assert_eq!(result.step_type, StepType::WebSocket);

    // Check response data
    if let Some(Value::Object(data)) = result.data {
        assert_eq!(data.get("action").unwrap().as_str().unwrap(), "send");
        assert_eq!(data.get("message"), Some(&message));
        assert_eq!(data.get("status").unwrap().as_str().unwrap(), "sent");
    } else {
        panic!("Expected response data");
    }
}

#[tokio::test]
async fn test_websocket_step_close() {
    let step = WebSocketStep::close(Some("test_connection".to_string()));

    let mut context = StepContext::new(Uuid::new_v4(), Uuid::new_v4());
    // Simulate existing connection
    context.variables.insert(
        "websocket_connection_test_connection".to_string(),
        Value::String("ws://localhost:8080/test".to_string())
    );
    
    let result = step.execute(&mut context).await.unwrap();
    
    assert!(result.success);
    assert_eq!(result.step_type, StepType::WebSocket);
    
    // Check that connection was removed from context
    assert!(!context.variables.contains_key("websocket_connection_test_connection"));
}

#[tokio::test]
async fn test_thinktime_step_fixed() {
    let step = ThinkTimeStep::fixed(0.1); // 100ms
    
    let mut context = StepContext::new(Uuid::new_v4(), Uuid::new_v4());
    let start = Instant::now();
    let result = step.execute(&mut context).await.unwrap();
    let elapsed = start.elapsed();

    assert!(result.success);
    assert_eq!(result.step_type, StepType::ThinkTime);
    assert!(elapsed >= Duration::from_millis(90)); // Allow some tolerance
    assert!(elapsed <= Duration::from_millis(200)); // Allow some tolerance

    // Check response data
    if let Some(Value::Object(data)) = result.data {
        assert_eq!(data.get("think_type").unwrap().as_str().unwrap(), "fixed");
        assert!(data.get("planned_duration").is_some());
        assert!(data.get("actual_duration").is_some());
    } else {
        panic!("Expected response data");
    }
}

#[tokio::test]
async fn test_thinktime_step_random() {
    let step = ThinkTimeStep::random(0.05, 0.15); // 50-150ms

    let mut context = StepContext::new(Uuid::new_v4(), Uuid::new_v4());
    let start = Instant::now();
    let result = step.execute(&mut context).await.unwrap();
    let elapsed = start.elapsed();
    
    assert!(result.success);
    assert_eq!(result.step_type, StepType::ThinkTime);
    assert!(elapsed >= Duration::from_millis(40)); // Allow some tolerance
    assert!(elapsed <= Duration::from_millis(200)); // Allow some tolerance
    
    // Check response data
    if let Some(Value::Object(data)) = result.data {
        assert_eq!(data.get("think_type").unwrap().as_str().unwrap(), "random");
        assert!(data.get("min_duration").is_some());
        assert!(data.get("max_duration").is_some());
    } else {
        panic!("Expected response data");
    }
}

#[tokio::test]
async fn test_transaction_step_start_end() {
    let start_step = TransactionStep::start("test_transaction".to_string());
    let end_step = TransactionStep::end("test_transaction".to_string());
    
    let mut context = StepContext::new(Uuid::new_v4(), Uuid::new_v4());

    // Start transaction
    let start_result = start_step.execute(&mut context).await.unwrap();
    assert!(start_result.success);
    assert_eq!(start_result.step_type, StepType::Transaction);

    // Check that transaction was stored in context
    assert!(context.variables.contains_key("transaction_test_transaction"));

    // Wait a bit
    tokio::time::sleep(Duration::from_millis(10)).await;

    // End transaction
    let end_result = end_step.execute(&mut context).await.unwrap();
    assert!(end_result.success);
    assert_eq!(end_result.step_type, StepType::Transaction);

    // Check that transaction was removed from context
    assert!(!context.variables.contains_key("transaction_test_transaction"));

    // Check that transaction result was stored
    assert!(context.variables.contains_key("transaction_result_test_transaction"));

    // Check response data
    if let Some(Value::Object(data)) = end_result.data {
        assert_eq!(data.get("transaction_name").unwrap().as_str().unwrap(), "test_transaction");
        assert_eq!(data.get("transaction_type").unwrap().as_str().unwrap(), "end");
        assert!(data.get("duration_ms").is_some());
        assert!(data.get("duration_seconds").is_some());
    } else {
        panic!("Expected response data");
    }
}

#[tokio::test]
async fn test_api_step() {
    // Create a temporary API file
    let mut temp_file = NamedTempFile::new().unwrap();
    writeln!(temp_file, "# Temporary API file for testing").unwrap();
    let api_path = temp_file.path().to_string_lossy().to_string();

    let step = ApiStep::with_export(
        api_path.clone(),
        vec!["extracted_user_id".to_string(), "extracted_token".to_string()]
    );

    let mut context = StepContext::new(Uuid::new_v4(), Uuid::new_v4());
    let result = step.execute(&mut context).await.unwrap();
    
    assert!(result.success);
    assert_eq!(result.step_type, StepType::API);
    
    // Check that variables were exported to context
    assert!(context.variables.contains_key("extracted_user_id"));
    assert!(context.variables.contains_key("extracted_token"));
    
    // Check response data
    if let Some(Value::Object(data)) = result.data {
        assert_eq!(data.get("api_path").unwrap().as_str().unwrap(), api_path);
        assert_eq!(data.get("status").unwrap().as_str().unwrap(), "simulated");
        assert!(data.get("exported_variables").is_some());
    } else {
        panic!("Expected response data");
    }
}

#[tokio::test]
async fn test_testcase_step() {
    // Create a temporary test case file
    let mut temp_file = NamedTempFile::new().unwrap();
    writeln!(temp_file, "# Temporary test case file for testing").unwrap();
    let testcase_path = temp_file.path().to_string_lossy().to_string();
    
    let step = TestCaseStep::with_export(
        testcase_path.clone(),
        vec!["extracted_session_id".to_string()]
    );
    
    let mut context = StepContext::new(Uuid::new_v4(), Uuid::new_v4());
    let result = step.execute(&mut context).await.unwrap();

    assert!(result.success);
    assert_eq!(result.step_type, StepType::TestCase);

    // Check that variables were exported to context
    assert!(context.variables.contains_key("extracted_session_id"));

    // Check response data
    if let Some(Value::Object(data)) = result.data {
        assert_eq!(data.get("testcase_path").unwrap().as_str().unwrap(), testcase_path);
        assert_eq!(data.get("status").unwrap().as_str().unwrap(), "simulated");
        assert!(data.get("exported_variables").is_some());
    } else {
        panic!("Expected response data");
    }
}

#[tokio::test]
async fn test_api_step_file_not_found() {
    let step = ApiStep::with_path("non_existent_file.yaml".to_string());

    let mut context = StepContext::new(Uuid::new_v4(), Uuid::new_v4());
    let result = step.execute(&mut context).await.unwrap();

    assert!(!result.success);
    assert!(result.error.unwrap().contains("not found"));
}

#[tokio::test]
async fn test_testcase_step_file_not_found() {
    let step = TestCaseStep::with_path("non_existent_testcase.yaml".to_string());

    let mut context = StepContext::new(Uuid::new_v4(), Uuid::new_v4());
    let result = step.execute(&mut context).await.unwrap();
    
    assert!(!result.success);
    assert!(result.error.unwrap().contains("not found"));
}
